package wili.misc;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import wili.a.Cache9;
import wili.a.Obj9;
import wili.c.Ca;
import wili.c.Class9a;
import wili.c.File9a;
import wili.c.Misc9a;

/**
 *
 */
public class HourTimes {
	public long startTime, curTime;

	public int allTimes;

	int[] times = { 0, 0, 0, 0, 0 };//月、周、日、时、分内的次数

	long[] inds = { 0, 0, 0, 0, 0 };//月、周，日，时，分的序号

	public String toString() {
		String st = "" + Misc9a.dateTimeStrOf(curTime);
		return st + ", times: " + " a" + times[0] + ", d" + times[2] + ", h" + times[3] + ", m" + times[4];
	}

	public HourTimes() {
		reset();
	}

	public HourTimes incTimes() {
		++allTimes;
		Calendar ca = Calendar.getInstance();
		curTime = ca.getTimeInMillis();
		long[] curInds = { ca.get(Calendar.MONTH), ca.get(Calendar.WEEK_OF_MONTH), ca.get(Calendar.DAY_OF_MONTH),
				ca.get(Calendar.HOUR_OF_DAY), ca.get(Calendar.MINUTE) };
		if (inds == null) {
			inds = curInds;
			Arrays.fill(times, 1);
			return this;
		}
		boolean found = false;
		for (int i = 0; i < curInds.length; i++) {
			if (inds[i] != curInds[i]) {
				found = true;
				for (int j = 0; j < i; j++) {
					++times[j];
				}
				for (int j = i; j < inds.length; j++) {
					times[j] = 1;
				}
				break;
			}
		}
		if (!found) {//increase within same minutes.
			for (int j = 0; j < times.length; j++) {
				++times[j];
			}
		}
		inds = curInds;
		return this;
	}

	public void reset() {
		startTime = System.currentTimeMillis();
		allTimes = 0;
		inds = null;
	}

	public static void test() {
		HourTimes obj = new HourTimes();
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					obj.incTimes();
					Ca.log(obj.toString());
					try {
						Thread.sleep((int) (3000 * Math.random()));
					} catch (InterruptedException e) {
						throw new RuntimeException(e);
					}
				}
			}
		}).start();
	}

	public static void main(String[] args) {
		test();
	}

	/**
	 *
	 */
	public static class Coms4 {
		public static String dateTimeSt() {
			return new Date().toString();
		}

		public static void listenHotConfigFile(File file, int interval, Consumer<String> action) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					File existFile = new File("C:\\t\\InsertTimeCostMethod_main\\listenHotConfigFile.txt");
					long lastTime = file.lastModified();
					long lastTime2 = existFile.lastModified();
					int interval2 = interval * 1000;
					int times = 0;
					try {
						while (true) {
							try {
								if (++times % 3 == 0) {
									if (lastTime2 != existFile.lastModified()) {
										if (File9a.strOfFile(existFile, "\n").trim().equals("q")) {
											Time9.log("\ntime9.exists." + times);
											return;
										}
										lastTime2 = existFile.lastModified();
									}
									System.out.print("time9." + times + "...");
								}
								Thread.sleep(interval2);
								if (file.lastModified() == lastTime)
									continue;
								lastTime = file.lastModified();
								String st = File9a.strOfFile(file, "\n");
								action.accept(st.trim());
							} catch (Throwable e) {
								e.printStackTrace();
							}
						}
					} finally {
					}
				}
			}).start();
		}

		public static void test_Time9() {
			Thread[] tt = new Thread[20];
			for (int i = 0; i < 20; i++) {
				final int j = i;
				Runnable run = new Runnable() {
					@Override
					public void run() {
						int times = (int) (10 * Math.random());
						for (int k = 0; k < times; k++) {
							try {
								Thread.sleep((long) (100 * Math.random()));
							} catch (InterruptedException e) {
								throw new RuntimeException(e);
							}
							Time9.start("wei" + j, null);
							try {
								Thread.sleep((long) (100 * Math.random()));
							} catch (InterruptedException e) {
								throw new RuntimeException(e);
							}
							Time9.stop("wei" + j);
						}
					}
				};
				tt[i] = new Thread(run);
				tt[i].start();
			}
			for (Thread th : tt) {
				try {
					th.join();
				} catch (InterruptedException e) {
					throw new RuntimeException(e);
				}
			}
			Time9.print();
		}

		public static void pause() {
		}
	}

	/**
	 * @author weila 2022年6月25日
	 */
	public static class Time9 {
		private static final Map<Object, Item> costMap = new ConcurrentHashMap<>();//id ~>[times, allCost, lastStart, lastEnd]

		static boolean USE = "".isEmpty();

		public static int idNum;

		static Set<String> idSet = new HashSet<>();
		static {
			resetIdSet();
			init();
		}

		public static void resetIdSet() {
			idSet.clear();
			idSet.addAll(Arrays.asList("JdbcAccessImpl.exec()", "BaseService.invoke()", "AppEnv.init()",
					"QuickQuery.getResultList()", "CountService.invoke()", "BaseService.invoke()", "TableLoader.init()", //
					"", "")
			//
			);
		}

		static boolean isSkipId(String id) {
			if ("456".isEmpty() && !idSet.contains(id))
				return true;
			return false;
		}

		public static synchronized void start(String id, Object obj, Object... args) {
			start(1, id, obj, args);
		}

		static boolean use(String kind) {//can be hot update when debugger running.
			return "".isEmpty();
		}

		public static synchronized void start(int opt, String id, Object obj, Object... args) {
			if (!use(""))
				return;
			if (id.startsWith("BoServiceLayer"))
				pause();
			//if (!USE) return;
			id = id.intern();
			if (isSkipId(id))
				return;
			if (costMap.size() > 2000) {
				clear();
			}
			interceptValues(args);
			Item ret = costMap.get(id);
			if (ret == null) {
				++idNum;
				costMap.put(id, ret = new Item(id, obj));
				return;
			}
			if (ret.startNum.incrementAndGet() == 1) {//上次未结束，这次又start. recursion is normal.
				ret.lastStart = System.currentTimeMillis();
				ret.lastEnd = 0;
			}
			if (obj != null && Misc9a.indexOf_identity(obj, ret.objSet) == -1)
				ret.objSet.add(obj);
		}
		//static AtomicBoolean started;

		static synchronized boolean init() {
			Time9.log("Time9.init...");
			StackTraceElement[] ee = new Exception().getStackTrace();
			String mainFile = ee[ee.length - 1].getFileName();
			if (mainFile.equals("tcli.java")) {
				Ca.pause();
			} else if (mainFile.equals("AppGate.java")) {
				Ca.pause();
			} else {
				Time9.log("Time9.init not, main file:" + mainFile);
				return false;
			}
			final File file = new File("c:/t/Time9.java.txt");
			Coms4.listenHotConfigFile(file, 3, new Consumer<String>() {
				@Override
				public void accept(String st) {
					Time9.findValue = "";
					Obj9.use(-1);
					if (st.equalsIgnoreCase("wili/c")) {
						Time9.clear();
						Time9.log("Time9.cleared.");
					} else if (st.equalsIgnoreCase("p")) {
						Time9.print();
					} else if (st.equals("s")) {
						USE = true;
						Time9.clear();
						Time9.log("Time9.started.");
					} else if (st.equals("S")) {
						USE = false;
						Time9.clear();
						Time9.log("Time9.stoped.");
					} else if (st.startsWith("po ")) {//print obj.
						st = st.substring(3).trim();
						Obj9.use(st.equals("y") ? 1 : -1);
						Time9.log("Time9.printObj: " + st);
					} else if (st.startsWith("pn ")) {//Obj9.PrintNull
						st = st.substring(3).trim();
						Obj9.PrintNull = st.equals("y");
						Time9.log("Time9.PrintNull: " + st);
					} else if (st.startsWith("f ")) {
						USE = true;
						Obj9.use(1);
						Obj9.curAction = Obj9.Act.find;
						Time9.findValue = st.substring(2).trim();
						Time9.log("Time9.find " + findValue + "...");
						return;
					} else if (st.startsWith("cache ")) {//use cache.
						st = st.substring(6).trim();
						if (st.startsWith("use=")) {
							Cache9.USE = st.substring(4).trim().equals("1");
						} else if (st.equals("clear")) {
							Cache9.clear();
						} else {
							Cache9.set(st);
							Cache9.print();
						}
					} else if (st.startsWith("set ")) {
						st = st.substring(4).trim();
						int ind = st.lastIndexOf('=');
						String va = st.substring(ind + 1).trim();
						ind = st.lastIndexOf('.');
						String cls = st.substring(0, ind);
						String fd = st.substring(ind + 1).trim();
						Class9a.setField(cls, fd, va);
					} else {
						Time9.log(
								"usage:\n p: print costMap\n c: print and clear costMap.\n s: start.\n S: stop.\n f abc:  find value contains 'abc'");
					}
				}
			});
			return true;
		}

		static void pause() {
			"".isEmpty();
		}

		public static synchronized void stop(String id) {
			if (!use(""))
				return;
			if (isSkipId(id))
				return;
			Item ret = costMap.get(id);
			if (ret == null) {
				log("\n Time9.NotStart: " + id);
				return;
			}
			int startNum = ret.startNum.decrementAndGet();
			if (startNum == 0) {
				ret.lastEnd = System.currentTimeMillis();
				++ret.times;
				ret.allCost += ret.lastEnd - ret.lastStart;
				ret.startNum.set(0);
			} else if (startNum > 0) {
				Coms4.pause();
			} else {
				log("\n Time9.AlreadyStopped(may concurrent): " + id);
			}
		}

		static int printNum = 0;

		static class Item implements Comparable<Item> {
			public Object id;

			public long times, allCost, lastStart, lastEnd;

			/**
			 * startNum: if >0 means has recursion ro concurrent.
			 */
			final AtomicInteger startNum = new AtomicInteger(1);

			public String toString() {
				// return "times:" + times + ",\tcost:" + allCost + "ms.";
				String st = objSet.isEmpty() ? "" : ", objNum=" + objSet.size();
				return "cost:" + allCost + "ms,\t" + times + "times,\tid: " + this.id + st;
			}

			/**
			 * 可显示在多少对象上进行了调用。
			 */
			List<Object> objSet = new ArrayList<>();

			public Item(Object id, Object obj) {
				this.id = id;
				if (obj != null)
					objSet.add(obj);
				this.lastStart = System.currentTimeMillis();
			}

			@Override
			public int compareTo(Item o) {
				return (int) (o.allCost - this.allCost);
			}
		}

		public static synchronized void print() {
			StringBuilder sb = new StringBuilder();
			++printNum;
			List<Item> list = new ArrayList<>(costMap.values());
			Collections.sort(list);
			sb.append("\n" + printNum + "=============================Time9.print: " + new Date().toString());
			sb.append("\n itemNum=" + list.size());
			for (int i = 0; i < list.size(); i++) {
				Item item = list.get(i);
				sb.append("\n " + (i + 1) + ", " + item.toString());
				if (item.allCost < 20) {
					sb.append("\n ......");
					break;
				}
			}
			sb.append("\n" + printNum + "=============================Time9.print:end.");
			String st = sb.toString().replace("__weilai345(", "(");
			log(st);
		}

		public static void use(int opt) {
			USE = opt == 1;
		}

		public static void clear() {
			print();
			costMap.clear();
		}

		static void log(String msg) {
			System.out.println(msg);
		}

		static String findValue = "";

		static Set objClsNameSet = new HashSet<>();

		public static void interceptValues(Object... values) {
			if ("".isEmpty())
				return;
			interceptValues("", values);
		}

		private static void interceptValues(String from, Object... values) {//intercept class, name, value...
			if (values == null)
				return;
			if (findValue.isEmpty() || values.length == 0)
				return;//cost much, so set "" to skip.
			for (int i = 0; i < values.length; i++) {
				Object arg = values[i];
				if (arg != null) {
					if (arg instanceof String || arg instanceof Number) {
						String st = arg.toString();
						if (st.contains(Time9.findValue)) {
							pause();//break point here, so can find given value.
						}
					} else {
						boolean print = false;
						//所有变化都起源于原始数据的变化，不必深入复杂对象。
						String cname = arg.getClass().getSimpleName();
						objClsNameSet.add("\n" + cname);
						switch (from) {
						case "OrmAccess":
						case "aa":
						case "bb":
						case "cc":
							print = true;
							break;
						}
						if (!print) {
							if (cname.startsWith("Bmo")) {
							} else {//TransactionCache
								switch (cname) {
								case "ViewServiceResponse":
									break;
								case "ViewTable":
									break;
								case "MetaService":
									Obj9.print(arg);
									break;
								case "QTable":
									break;
								case "SqlBuilder":
									break;
								case "Table":
									break;
								case "UdfObject":
									break;
								case "UdfTable":
									break;
								default:
									continue;
								}
							}
							print = true;
						}
						//Obj9.print(arg);
						if ("".isEmpty() && Obj9.find("", Time9.findValue, arg)) {
							pause();
							Time9.findValue = "";
						}
					}
				}
			}
		}

		static void test() {
			Coms4.test_Time9();
		}

		public static void main(String[] args) {
			test();
		}
	}
}